#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#include <iostream>
#include "Encoding/Encoding.h"
#include "NamedPipe.h"

using namespace SystemTool;

// Construct the db
NamedPipe::NamedPipe() : m_hPipe(0)
{
}

// Destruct the db
NamedPipe::~NamedPipe()
{
}

// Create NamedPipe
bool NamedPipe::Create(bool bIsServer, std::string strName)
{
#ifdef _WIN32
    std::string strPipeName = std::string("\\\\.\\Pipe\\") + strName;
    std::wstring strFinalName = SystemTool::Encoding::UTF8ToUTF16(strPipeName);
    if(bIsServer)
    {
        // Create pipe for read
        m_hPipe = CreateNamedPipeW(
            strFinalName.c_str(),                                  // 管道名称
            PIPE_ACCESS_DUPLEX|| FILE_FLAG_READ,                   // 管道类型
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, // 管道参数
            PIPE_UNLIMITED_INSTANCES,                              // 管道能创建的最大实例个数
            0,                                                     // 输出缓冲区大小，由系统决定
            0,                                                     // 输入缓冲区大小，由系统决定
            NMPWAIT_WAIT_FOREVER,                                  // 超时时间
            NULL);                                                 // 指定security_aatr结构参数
        if (m_hPipe == INVALID_HANDLE_VALUE)
        {
            std::cerr << "CreatePipe failed: " << GetLastError() << std::endl;
            return false;
        }

        // Wait for client connected
        if (!ConnectNamedPipe(m_hPipe, NULL))
        {
            std::cerr << "ConnectNamedPipe failed: " << GetLastError() << std::endl;
            CloseHandle(m_hPipe);
            return false;
        }
    }
    else
    {
        // Connect to pipe server to know wether it is ready created
        BOOL bRet = WaitNamedPipeW(strFinalName.c_str(), NMPWAIT_WAIT_FOREVER);
        if(!bRet)
        {
            std::cerr << "Connect to NamedPipe failed: " << GetLastError() << std::endl;
            CloseHandle(m_hPipe);
            return false;
        }

        // Open the pipe for write
        m_hPipe = CreateFileW(
            strFinalName.c_str(),         // 管道名称
            GENERIC_WRITE,                // 访问模式
            0,                            // 共享模式
            NULL,                         // 安全属性
            OPEN_EXISTING,                // 创建选项
            FILE_ATTRIBUTE_NORMAL,        // 文件属性
            NULL);
        if (m_hPipe == INVALID_HANDLE_VALUE)
        {
            std::cerr << "Open existed pipe failed: " << GetLastError() << std::endl;
            return false;
        }
    }
#else
    std::string strPipeName = std::string("/tmp/com.") + strName + ".fifo";
    if(bIsServer)
    {
        int iRet = mkfifo(strPipeName.c_str(), 0777);
        if (iRet != 0)
        {
            std::cerr << "mkfifo failed: " << iRet << std::endl;
            return false;
        }
        
        // Open the pipe for read 
        m_hPipe = open(strPipeName.c_str(), O_RDONLY);
        if (m_hPipe == -1)
        {
            std::cerr << "open read pipe failed: " << m_hPipe << std::endl;
            return false;
        }
    }
    else
    {
        // Open the pipe for write 
        m_hPipe = open(strPipeName.c_str(), O_WRONLY);
        if (m_hPipe == -1)
        {
            std::cerr << "open write pipe failed: " << m_hPipe << std::endl;
            return false;
        }
    }
#endif
    return true;
}

// Read data
int NamedPipe::ReadBytes(char *pData, int iDataSize)
{
    if (m_hPipe == 0)
    {
        return 0;
    }
#ifdef _WIN32
    DWORD dwBytesRead = 0;
    if (!ReadFile(m_hPipe, pData, iDataSize, &dwBytesRead, NULL))
    {
        std::cerr << "ReadFile failed: " << GetLastError() << std::endl;
        CloseHandle(m_hPipe);
        return 0;
    }
#else
    int dwBytesRead = read(m_hPipe, pData, iDataSize);
    if (dwBytesRead == -1)
    {
        std::cerr << "read failed: " << errno << std::endl;
        close(m_hPipe);
        return 0;
    }
#endif
    return dwBytesRead;
}

// Write data
int NamedPipe::WriteBytes(const char *pData, int iDataSize)
{
    if (m_hPipe == 0)
    {
        return 0;
    }
#ifdef _WIN32
    DWORD dwBytesWrite = 0;
    if (!WriteFile(m_hPipe, pData, iDataSize, &dwBytesWrite, NULL))
    {
        std::cerr << "WriteFile failed: " << GetLastError() << std::endl;
        CloseHandle(m_hPipe);
        return 0;
    }
#else
    int dwBytesWrite = write(m_hPipe, pData, iDataSize);
    if (dwBytesWrite == -1)
    {
        std::cerr << "read failed: " << errno << std::endl;
        close(m_hPipe);
        return 0;
    }
#endif
    return dwBytesWrite;
}

// Destroy NamedPipe
bool NamedPipe::Destroy()
{
    if (m_hPipe)
    {
#ifdef _WIN32
        // Blocked util the other one finished the read or write
        FlushFileBuffers(hPipe);
       
        // If you want to close the pipe,please confirm another one has finished their read or write
        if(!DisconnectNamedPipe(hPipe))
        {
            std::cerr << "DisconnectNamedPipe failed: " << GetLastError() << std::endl;
            return false;
        }
        CloseHandle(m_hPipe);
#else
        close(m_hPipe);
#endif
    }
    return true;
}
